package cn.piesat.filter;

import cn.piesat.config.AppConfig;
import cn.piesat.util.AddressUtil;
import cn.piesat.util.JsonUtil;
import cn.piesat.util.RouteIdUtil;
import cn.piesat.util.UriUtil;
import cn.piesat.vo.RecordDO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author: demo
 * @date: 2020年7月2日
 * @Document : 请求记录过滤器
 */
@Component
public class RecordGlobalFilter implements GlobalFilter, Ordered {
	private  static  final   String  SUPPORT_SCHEME_HTTP="http";
	private  static  final   String  SUPPORT_SCHEME_HTTPS="https";

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest originalRequest = exchange.getRequest();
		URI originalRequestUrl = originalRequest.getURI();
		// 只记录http的请求
		String scheme = originalRequestUrl.getScheme();
		if ((!SUPPORT_SCHEME_HTTP.equals(scheme) && !SUPPORT_SCHEME_HTTPS.equals(scheme))) {
			return chain.filter(exchange);
		}
		RecordDO record = new RecordDO(RouteIdUtil.nextStringId());
		Route route=exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
		URI gatewayUri=route.getUri();
		record.setGatewayAddress(UriUtil.getUriAddress(gatewayUri));
		/**
		 * 重写request 和response
		 */
		RecordHttpRequestDecorator recordRequest = new RecordHttpRequestDecorator(exchange.getRequest());
		RecordHttpResponseDecorator recordResponse = new RecordHttpResponseDecorator(exchange.getResponse(), record);

		ServerWebExchange recordExchange = exchange.mutate().request(recordRequest).response(recordResponse).build();

		Mono<Void>  mono= recorderOriginalRequest( recordExchange, record).then(chain.filter(recordExchange)).then();
		return mono;
	}

	/**
	 * 记录请求信息
	 * 获取请求信息  ==>record
	 * url method header param startTime
	 * @param exchange
	 * @param record
	 * @return
	 */
	private Mono<Void> recorderOriginalRequest(ServerWebExchange exchange, RecordDO record) {
		ServerHttpRequest request = exchange.getRequest();
		URI requestUri=request.getURI();
		record.setUrl(requestUri.getRawPath());

		record.setRequestAddress(UriUtil.getUriAddress(requestUri));
		record.setMethod( request.getMethod().name());
		HttpHeaders headers = request.getHeaders();
		record.setClientAddress(RecordTool.getClientIp(request));
		record.setLocatoion(AddressUtil.getCityInfo(record.getClientAddress()));
		record.setRequestHeader(JsonUtil.toString(headers));
		record.setStartTime(new Date());
		String userName = headers.getFirst("userName");
		record.setUserName(userName);
		record.setType(2);
		//get方式请求获取参数，post方式请求为空
		String que = requestUri.getQuery();
		//针对登录接口特殊处理，参数中获取用户名和修改日志状态
		String pa = que;
        if(record.getUrl().equals(AppConfig.TOKEN_URL)){
            record.setType(1);
            if(StringUtils.isNotBlank(que)){
				String [] strs =que.split("&");
				List<String> list = new ArrayList<>();
				if(strs.length>1){
					record.setUserName(strs[0].replace("username=",""));
					for(int i=0;i<strs.length;i++){
						if(i != 1){
							list.add(strs[i]);
						}
					}
					pa = list.toString();
				}
			}

        }
        //登录时从参数中去除密码敏感信息
		record.setRequestParam(pa);
		Charset requestCharset = RecordTool.getCharsetRecord(headers);
		Mono<Void> result = Mono.empty();
		//当post请求中包含json和？a=**之类的参数，将参数拼接
		if (requestCharset != null) {
		 result = DataBufferUtils.join(request.getBody()).doOnNext(buffer -> {
				CharBuffer charBuffer = requestCharset.decode(buffer.asByteBuffer());
                String param = charBuffer.toString();
				param = que+param;
				record.setRequestParam(param);
				DataBufferUtils.release(buffer);
			}).then();
		}
		return result;
	}

	@Override
	public int getOrder() {
		return -1;
	}

}